//
//  $Id: NSString+XMLAdditions.m 2 2007-09-07 18:35:43Z samson.etienne $
//  TCUBridgedClasses
//
//  Created by Simon Stapleton on Wed Oct 30 2002.
//  Copyright (c) 2002 Simon Stapleton. All rights reserved.
//

#import "NSString+XMLAdditions.h"

@implementation NSString (XMLAdditions)
/*" A few functions for dealing with XML.  Nothing particularly spectacular, but useful for our purposes. "*/

/*" Is this string valid for use as an XML element name?

XML Rules specify the following:

%{First character must be a letter or underscore, followed by any number of letters, numbers, hyphens, periods or underscores.}

%{May not start with the literal "xml".}

%{Colons are only allowable for specifying namespaces.}

The same rules apply for attribute names, so we'll reuse this method for that purpose too.
"*/
- (BOOL) isValidXMLElementName {
  BOOL valid = NO;

  if ([self length]) {
    id firstCharacterSet = [[[NSMutableCharacterSet alloc] init] autorelease];
    id restCharacterSet = [[[NSMutableCharacterSet alloc] init] autorelease];
    id namePart = [[self componentsSeparatedByString:@":"] lastObject];
    
    [firstCharacterSet formUnionWithCharacterSet:[NSCharacterSet letterCharacterSet]];
    [firstCharacterSet addCharactersInString:@"_"];
    [restCharacterSet formUnionWithCharacterSet:firstCharacterSet];
    [restCharacterSet formUnionWithCharacterSet:[NSCharacterSet decimalDigitCharacterSet]];
    [restCharacterSet addCharactersInString:@"-."];
    [restCharacterSet invert];

    valid = ([[self componentsSeparatedByString:@":"] count] <= 2);
    valid = valid && [firstCharacterSet characterIsMember:[namePart characterAtIndex:0]];
    valid = valid && ([namePart rangeOfCharacterFromSet:restCharacterSet].location == NSNotFound);
    valid = valid && ([namePart rangeOfString:@"xml"].location != 0);
  }
  return valid;
}

/*" Is this string valid for use as an XML attribute value?

There's no hard and fast rules as to what an attribute can hold, but we don't want any illegal characters, controls or newlines, so we will check for those at least.  As for the rest - anything goes.
"*/
- (BOOL) isValidXMLAttributeValue {
  NSMutableCharacterSet * chars = [[[NSMutableCharacterSet alloc] init] autorelease];
  [chars formUnionWithCharacterSet:[NSCharacterSet controlCharacterSet]];
  [chars formUnionWithCharacterSet:[NSCharacterSet illegalCharacterSet]];
  [chars addCharactersInString:@"\n"];
  return [self rangeOfCharacterFromSet:chars].location == NSNotFound;
}

/*" Return a copy of this string with XML entities quoted.  Adapted from code by Scott Anguish (sanguish@digifix.com) posted to cocoadev mailing list "*/
- (id) stringByQuotingXMLEntities {
/*  NSMutableString * mutable = [[self mutableCopy] autorelease];
  [mutable replaceOccurrencesOfString:@"&" withString:@"&amp;"
                              options:NSLiteralSearch
                                range:NSMakeRange(0, [mutable length])];
  [mutable replaceOccurrencesOfString:@"<" withString:@"&lt;"
                              options:NSLiteralSearch
                                range:NSMakeRange(0, [mutable length])];
  [mutable replaceOccurrencesOfString:@">" withString:@"&gt;"
                              options:NSLiteralSearch
                                range:NSMakeRange(0, [mutable length])];
  [mutable replaceOccurrencesOfString:@"\"" withString:@"&quot;"
                              options:NSLiteralSearch
                                range:NSMakeRange(0, [mutable length])];
  [mutable replaceOccurrencesOfString:@"'" withString:@"&apos;"
                              options:NSLiteralSearch
                                range:NSMakeRange(0, [mutable length])];
  
  return [[mutable copy] autorelease];*/
    return [(NSString*)CFXMLCreateStringByEscapingEntities(NULL,(CFStringRef)self, NULL) autorelease];
}

- (id) stringByUnquotingXMLEntities {
    return [(NSString*)CFXMLCreateStringByUnescapingEntities(NULL,(CFStringRef)self, NULL) autorelease];
}

/*"Generate an XML attribute list from dictionary.

%{Dictionary entries whose #{-description} does not return a valid string (either for name or value) will be ignored.}"*/
+ (NSString *) xmlAttributeStringFromDictionary: (NSDictionary *) dictionary {
  NSMutableString * theString = [NSMutableString string];
  NSEnumerator * enumerator = [[dictionary allKeys] objectEnumerator];
  id key;

  while ((key = [enumerator nextObject])) {
    id keyString = [key description];
    id valueString = [[[dictionary objectForKey:key] description] stringByQuotingXMLEntities];
    if ([keyString isValidXMLElementName] && [valueString isValidXMLAttributeValue]) {
      [theString appendString:[NSString stringWithFormat:@" %@=\"%@\"", keyString, valueString]];
    }
  }
  return [theString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
@end
